library(ggpubr)
Loading required package: magrittr
Attaching package: ‘magrittr’
The following object is masked from ‘package:purrr’:
set_names
The following object is masked from ‘package:tidyr’:
extract
Attaching package: ‘ggpubr’
The following object is masked from ‘package:cowplot’:
get_legend
f74.rna.seu@meta.data <- f74.rna.seu@meta.data %>%
rownames_to_column("cell") %>%
left_join(annotation.df) %>%
column_to_rownames("cell")
Joining, by = "cell"
First I have made the snap file from the cellranger fragments.tsv file. Script is multiOmic_benchmark/preprocess/fragments2snap.sh.
Following integration vignette
Barcode selection
Filtering based on number of reads per cell and ratio of fragments that are within promoters

barcode.ls = lapply(seq(snap.files), function(i){
barcodes = barcode.ls[[i]];
idx = which(
barcodes$logUMI >= cutoff.logUMI.low[i] &
barcodes$logUMI <= cutoff.logUMI.high[i] &
barcodes$promoter_ratio >= cutoff.FRIP.low[i] &
barcodes$promoter_ratio <= cutoff.FRIP.high[i]
);
barcodes[idx,]
});
x.sp.ls = lapply(seq(snap.files), function(i){
barcodes = barcode.ls[[i]];
x.sp = x.sp.ls[[i]];
barcode.shared = intersect(x.sp@barcode, barcodes$barcode);
x.sp = x.sp[match(barcode.shared, x.sp@barcode),];
barcodes = barcodes[match(barcode.shared, barcodes$barcode),];
x.sp@metaData = barcodes;
x.sp
})
names(x.sp.ls) = sample.names;
x.sp = Reduce(snapRbind, x.sp.ls);
x.sp@metaData["sample"] = x.sp@sample;
x.sp
number of barcodes: 6043
number of bins: 0
number of genes: 0
number of peaks: 0
number of motifs: 0
Add cell-by-bin matrix
x.sp = addBmatToSnap(x.sp, bin.size = 5000)
Epoch: reading cell-bin count matrix session ...
Binarize matrix
Some items in the count matrix have abnormally high coverage perhaps due to the alignment errors. Therefore, we next remove top 0.1% items in the count matrix and then convert the remaining non-zero values to 1.
x.sp = makeBinary(x.sp, mat="bmat")
x.sp
number of barcodes: 6043
number of bins: 651791
number of genes: 0
number of peaks: 0
number of motifs: 0
Filter bins
Filter out bins overlapping w ENCODE blacklist
black_list = read.table("~/annotations/hg38.blacklist.bed.gz")
black_list.gr = GRanges(
black_list[,1],
IRanges(black_list[,2], black_list[,3])
);
idy = queryHits(
findOverlaps(x.sp@feature, black_list.gr)
);
if(length(idy) > 0){
x.sp = x.sp[,-idy, mat="bmat"];
};
x.sp
number of barcodes: 6043
number of bins: 651765
number of genes: 0
number of peaks: 0
number of motifs: 0
Exclude bad chromosomes
chr.exclude = seqlevels(x.sp@feature)[grep("random|chrM", seqlevels(x.sp@feature))]
idy = grep(paste(chr.exclude, collapse="|"), x.sp@feature)
if(length(idy) > 0){
x.sp = x.sp[,-idy, mat="bmat"]
}
x.sp
number of barcodes: 6043
number of bins: 650342
number of genes: 0
number of peaks: 0
number of motifs: 0
remove the top 5% bins that overlap with invariant features such as the house keeping gene promoters
bin.cov = log10(Matrix::colSums(x.sp@bmat)+1)
# bin.cov = Matrix::colSums(x.sp@bmat)
hist(
bin.cov[bin.cov > 0],
xlab="log10(bin cov)",
main="log10(Bin Cov)",
col="lightblue",
# xlim=c(0, 5),
breaks=100
);

bin.cutoff = quantile(bin.cov[bin.cov > 0], 0.95)
idy = which(bin.cov <= bin.cutoff & bin.cov > 0)
x.sp = x.sp[, idy, mat="bmat"];
x.sp
number of barcodes: 6043
number of bins: 539584
number of genes: 0
number of peaks: 0
number of motifs: 0
Remove any cells of bin coverage less than 1,000. The rational behind this is that some cells may have high number of unique fragments but end up with low bin coverage after filtering. This step is optional but highly recommanded.
idx = which(Matrix::rowSums(x.sp@bmat) > 1000);
x.sp = x.sp[idx,];
x.sp
number of barcodes: 5793
number of bins: 539584
number of genes: 0
number of peaks: 0
number of motifs: 0
Dimensionality reduction
Uses diffusion map algorithm w sampling technique to make it fast.
## Sample 100 cells as landmarks
row.covs.dens <- density(
x = x.sp@metaData[,"logUMI"],
bw = 'nrd', adjust = 1
)
sampling_prob <- 1 / (approx(x = row.covs.dens$x, y = row.covs.dens$y, xout = x.sp@metaData[,"logUMI"])$y + .Machine$double.eps);
set.seed(1)
idx.landmark.ds <- sort(sample(x = seq(nrow(x.sp)), size = 1000, prob = sampling_prob))
## Split between landmark and query cells
x.landmark.sp = x.sp[idx.landmark.ds,];
x.query.sp = x.sp[-idx.landmark.ds,];
## Run diffusion map on landmark
x.landmark.sp = runDiffusionMaps(
obj= x.landmark.sp,
input.mat="bmat",
num.eigs=50
);
Epoch: checking the inputs ...
Epoch: computing jaccard similarity matrix ...
Epoch: fitting regression model ...
Epoch: performing normalization ...
Epoch: computing eigen decomposition ...
Epoch: Done
x.landmark.sp@metaData$landmark = 1;
## Project query cells
x.query.sp = runDiffusionMapsExtension(
obj1=x.landmark.sp,
obj2=x.query.sp,
input.mat="bmat"
)
Epoch: checking the inputs ...
Epoch: computing jaccard similarity matrix ...
Epoch: performing normalization ...
Epoch: projecting query cells to the reference ...
Epoch: Done
x.query.sp@metaData$landmark = 0;
## Combine
x.sp = snapRbind(x.landmark.sp, x.query.sp);
'rBind' is deprecated.
Since R version 3.2.0, base's rbind() should work fine with S4 objects
x.sp = x.sp[order(x.sp@metaData[,"sample"])]; #IMPORTANT
To determine significant diffusion components: > We use an ad hoc method by simply looking at a pairwise plot and select the number of eigen vectors that the scatter plot starts looking like a blob. In the below example, we choose the first 15 eigen vectors.
plotDimReductPW(
obj=x.sp,
eigs.dims=1:50,
point.size=0.3,
point.color="grey",
point.shape=19,
point.alpha=0.6,
down.sample=5000,
pdf.file.name=NULL,
pdf.height=7,
pdf.width=7
);

Clustering and visualization
x.sp=runCluster(
obj=x.sp,
tmp.folder=tempdir(),
louvain.lib="leiden",
seed.use=10,
resolution=0.7
);
Epoch: checking input parameters
Epoch: finding clusters using leiden
Visualization

Make gene matrix
saveRDS("~/my_data/cellranger-atac110_count_30439_WSSS8038360_GRCh38-1_1_0.snapATAC.RDS")
Error in saveRDS("~/my_data/cellranger-atac110_count_30439_WSSS8038360_GRCh38-1_1_0.snapATAC.RDS") :
'file' must be non-empty string
Repeat visualization on gmat

FeaturePlot(f74.seu, features = c('PTPRC','CD4','CD8A','CD8B','CD79A','FOXN1','EPCAM','PDGFRA','GNG4', 'FOXP3','RAG1','RAG2','NKG7','CCR7'), reduction = "umap.snap")
All cells have the same value (0) of CD79A.All cells have the same value (0) of RAG2.All cells have the same value (0) of NKG7.All cells have the same value (0) of CCR7.
DimHeatmap(f74.seu, dims = 1:6, balanced = TRUE,ncol = 1)


31 Oct 2019 09:28:40 [rsession-] CLIENT EXCEPTION (rsession-jovyan): (TypeError) : Cannot read property 'a' of null;
org/rstudio/studio/client/workbench/views/source/editors/text/ChunkPlotPage.java#52::execute
org/rstudio/studio/client/workbench/views/source/editors/text/ChunkPlotWidget.java#49::onBrowserEvent
com/google/gwt/user/client/DOM.java#1414::dispatchEvent
com/google/gwt/user/client/impl/DOMImplStandard.java#312::dispatchEvent
com/google/gwt/user/client/impl/DOMImplStandard.java#334::dispatchUnhandledEvent
com/google/gwt/core/client/impl/Impl.java#244::apply
com/google/gwt/core/client/impl/Impl.java#283::entry0
rstudio-0.js#-1::eval
Client-ID: 7beb3877-891d-4e16-98d5-f8cca1f72d5d
User-Agent: Mozilla/5.0 (Macintosh Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36

Differential analysis

Expression of markers in RNA data
map(1:10, ~ plot_cluster_marker_ATACvsRNA(.x))
All cells have the same value (0) of MIR4300HG.All cells have the same value (0) of NOS1.All cells have the same value (0) of RPH3A.Cannot convert object of class listggarrange into a grob.All cells have the same value (0) of C5orf66-AS2.All cells have the same value (0) of CYP3A43.
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]
[[8]]
[[9]]
[[10]]










Embedding on most variable genes in ATAC (not in gene expression)

Thoughts
- There seems to be a correspondance for gene expression and accessibility in many “accessibility markers” (see Cluster 6) –> maybe integration would work better if I don’t take only the most variable features in the RNA data.
LS0tCnRpdGxlOiAiU25hcCBBVEFDIGRhdGEgcHJvY2Vzc2luZyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyh7CiAgbGlicmFyeShTbmFwQVRBQykKICBsaWJyYXJ5KGxlaWRlbikKICBsaWJyYXJ5KHVtYXApCiAgbGlicmFyeShHZW5vbWljUmFuZ2VzKQogIGxpYnJhcnkoem9vKQogIGxpYnJhcnkodGlkeXZlcnNlKQogIGxpYnJhcnkoY293cGxvdCkKICBsaWJyYXJ5KGdncHVicikKfSkKc291cmNlKCJ+L211bHRpT21pY19iZW5jaG1hcmsvcHJlcHJvY2Vzcy9zZWxlY3RGZWF0dXJlcy5SIikKYGBgCmBgYHtyfQojIyBMb2FkIGFuZCBlbWJlZCBSTkEgZGF0YQpmNzQuc2NlLmxpc3QgPC0gcmVhZFJEUygifi9teV9kYXRhL2ludGVncmF0ZWRfdGh5bXVzL0Y3NF9TQ0VsaXN0XzIwMTkxMDE3LlJEUyIpCmY3NC5ybmEgPC0gZjc0LnNjZS5saXN0JFJOQQoKZjc0LnJuYS5zZXUgPC0gYXMuU2V1cmF0KGY3NC5ybmEsIHZlcmJvc2U9RkFMU0UpCmludGVncmF0ZV9mZWF0dXJlcyA8LSBIVkdfU2V1cmF0KGY3NC5ybmEsIG5mZWF0dXJlcyA9IDMwMDApClZhcmlhYmxlRmVhdHVyZXMoZjc0LnJuYS5zZXUpIDwtIGludGVncmF0ZV9mZWF0dXJlcwpmNzQucm5hLnNldSA8LSBTY2FsZURhdGEoZjc0LnJuYS5zZXUsIGRvLmNlbnRlciA9IFRSVUUsIHZlcmJvc2U9RkFMU0UpCmY3NC5ybmEuc2V1IDwtIFJ1blBDQShmNzQucm5hLnNldSkgCmY3NC5ybmEuc2V1IDwtIFJ1blVNQVAoZjc0LnJuYS5zZXUsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zPTE6NTApIAoKIyMgQWRkIGNlbGwgdHlwZSBhbm5vdGF0aW9uCmFubm90YXRpb24uZGYgPC0gcmVhZC5jc3YoIn4vbXlfZGF0YS9GNzRfUk5BX29icy5jc3YiKQphbm5vdGF0aW9uLmRmIDwtIGFubm90YXRpb24uZGYgJT4lCiAgbXV0YXRlKGNlbGw9c3RyX3JlbW92ZShhcy5jaGFyYWN0ZXIoWCksICJGNzRfMV8iKSAlPiUgc3RyX2MoaWZlbHNlKGJhdGNoPT0wLCdfMScsICJfMiIpKSkgCgpmNzQucm5hLnNldUBtZXRhLmRhdGEgPC0gZjc0LnJuYS5zZXVAbWV0YS5kYXRhICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbCIpICU+JQogIGxlZnRfam9pbihhbm5vdGF0aW9uLmRmKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoImNlbGwiKQoKYGBgCgoKRmlyc3QgSSBoYXZlIG1hZGUgdGhlIHNuYXAgZmlsZSBmcm9tIHRoZSBjZWxscmFuZ2VyIGBmcmFnbWVudHMudHN2YCBmaWxlLiBTY3JpcHQgaXMgYG11bHRpT21pY19iZW5jaG1hcmsvcHJlcHJvY2Vzcy9mcmFnbWVudHMyc25hcC5zaGAuCgpGb2xsb3dpbmcgW2ludGVncmF0aW9uIHZpZ25ldHRlXShodHRwczovL2dpdGh1Yi5jb20vcjNmYW5nL1NuYXBBVEFDL2Jsb2IvbWFzdGVyL2V4YW1wbGVzLzEwWF9QQk1DXzE1Sy9SRUFETUUubWQpIAoKIyMjIEJhcmNvZGUgc2VsZWN0aW9uCkZpbHRlcmluZyBiYXNlZCBvbiBudW1iZXIgb2YgcmVhZHMgcGVyIGNlbGwgYW5kIHJhdGlvIG9mIGZyYWdtZW50cyB0aGF0IGFyZSB3aXRoaW4gcHJvbW90ZXJzCmBgYHtyfQpzbmFwLmZpbGVzIDwtICJ+L215X2RhdGEvY2VsbHJhbmdlci1hdGFjMTEwX2NvdW50XzMwNDM5X1dTU1M4MDM4MzYwX0dSQ2gzOC0xXzFfMC5zbmFwIgpzYW1wbGUubmFtZXMgPC0gIkY3NCIKYmFyY29kZS5maWxlcyA8LSAnfi9teV9kYXRhL3NpbmdsZWNlbGwuY3N2JwoKeC5zcC5scyA8LSBsaXN0KGNyZWF0ZVNuYXAoc25hcC5maWxlcywgc2FtcGxlLm5hbWVzKSkKbmFtZXMoeC5zcC5scykgPSBzYW1wbGUubmFtZXMKeC5zcC5scwoKYmFyY29kZS5scyA9IGxhcHBseShzZXEoc25hcC5maWxlcyksIGZ1bmN0aW9uKGkpewogICAgYmFyY29kZXMgPSByZWFkLmNzdigKICAgICAgICBiYXJjb2RlLmZpbGVzW2ldLCAKICAgICAgICBoZWFkPVRSVUUKICAgICk7CiAgICAjIHJlbW92ZSBOTyBCQVJPQ0RFIGxpbmUKICAgIGJhcmNvZGVzID0gYmFyY29kZXNbMjpucm93KGJhcmNvZGVzKSxdOwogICAgYmFyY29kZXMkbG9nVU1JID0gbG9nMTAoYmFyY29kZXMkcGFzc2VkX2ZpbHRlcnMgKyAxKTsKICAgIGJhcmNvZGVzJHByb21vdGVyX3JhdGlvID0gKGJhcmNvZGVzJHByb21vdGVyX3JlZ2lvbl9mcmFnbWVudHMrMSkgLyAoYmFyY29kZXMkcGFzc2VkX2ZpbHRlcnMgKyAxKTsKICAgIGJhcmNvZGVzCiAgfSkKCnBsb3RzID0gbGFwcGx5KHNlcShzbmFwLmZpbGVzKSwgZnVuY3Rpb24oaSl7CiAgICBwMSA9IGdncGxvdCgKICAgICAgICBiYXJjb2RlLmxzW1tpXV0sIAogICAgICAgIGFlcyh4PWxvZ1VNSSwgeT1wcm9tb3Rlcl9yYXRpbykpICsgCiAgICAgICAgZ2VvbV9wb2ludChzaXplPTAuMywgY29sPSJncmV5IikgKwogICAgICAgIHRoZW1lX2NsYXNzaWMoKQkrCiAgICAgICAgZ2d0aXRsZShzYW1wbGUubmFtZXNbW2ldXSkgKwogICAgICAgIHlsaW0oMCwgMSkgKyB4bGltKDAsIDYpICsgCiAgICAgICAgbGFicyh4ID0gImxvZzEwKGNvdW50cykiLCB5PSJwcm9tb3RlciByYXRpbyIpCiAgICAgICAgcDEKICAgIH0pCgojIyBTZWxlY3QgYW5kIHZpeiBjdXRvZmZzCmN1dG9mZi5sb2dVTUkubG93ID0gMy4zCmN1dG9mZi5sb2dVTUkuaGlnaCA9IDQuOApjdXRvZmYuRlJJUC5sb3cgPSAwLjI1CmN1dG9mZi5GUklQLmhpZ2ggPSAwLjY1CgoKcGxvdHNbWzFdXSArIAogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoY3V0b2ZmLmxvZ1VNSS5sb3dbMV0sY3V0b2ZmLmxvZ1VNSS5oaWdoWzFdKSwgbGluZXR5cGU9MikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGMoY3V0b2ZmLkZSSVAubG93WzFdLGN1dG9mZi5GUklQLmhpZ2hbMV0pLCBsaW5ldHlwZT0yKQpgYGAKYGBge3J9CmJhcmNvZGUubHMgPSBsYXBwbHkoc2VxKHNuYXAuZmlsZXMpLCBmdW5jdGlvbihpKXsKICBiYXJjb2RlcyA9IGJhcmNvZGUubHNbW2ldXTsKICBpZHggPSB3aGljaCgKICAgICAgYmFyY29kZXMkbG9nVU1JID49IGN1dG9mZi5sb2dVTUkubG93W2ldICYgCiAgICAgIGJhcmNvZGVzJGxvZ1VNSSA8PSBjdXRvZmYubG9nVU1JLmhpZ2hbaV0gJiAKICAgICAgYmFyY29kZXMkcHJvbW90ZXJfcmF0aW8gPj0gY3V0b2ZmLkZSSVAubG93W2ldICYKICAgICAgYmFyY29kZXMkcHJvbW90ZXJfcmF0aW8gPD0gY3V0b2ZmLkZSSVAuaGlnaFtpXQogICk7CiAgYmFyY29kZXNbaWR4LF0KfSk7Cnguc3AubHMgPSBsYXBwbHkoc2VxKHNuYXAuZmlsZXMpLCBmdW5jdGlvbihpKXsKICBiYXJjb2RlcyA9IGJhcmNvZGUubHNbW2ldXTsKICB4LnNwID0geC5zcC5sc1tbaV1dOwogIGJhcmNvZGUuc2hhcmVkID0gaW50ZXJzZWN0KHguc3BAYmFyY29kZSwgYmFyY29kZXMkYmFyY29kZSk7CiAgeC5zcCA9IHguc3BbbWF0Y2goYmFyY29kZS5zaGFyZWQsIHguc3BAYmFyY29kZSksXTsKICBiYXJjb2RlcyA9IGJhcmNvZGVzW21hdGNoKGJhcmNvZGUuc2hhcmVkLCBiYXJjb2RlcyRiYXJjb2RlKSxdOwogIHguc3BAbWV0YURhdGEgPSBiYXJjb2RlczsKICB4LnNwCn0pCm5hbWVzKHguc3AubHMpID0gc2FtcGxlLm5hbWVzOwp4LnNwID0gUmVkdWNlKHNuYXBSYmluZCwgeC5zcC5scyk7Cnguc3BAbWV0YURhdGFbInNhbXBsZSJdID0geC5zcEBzYW1wbGU7Cnguc3AKYGBgCgojIyMgQWRkIGNlbGwtYnktYmluIG1hdHJpeApgYGB7cn0KeC5zcCA9IGFkZEJtYXRUb1NuYXAoeC5zcCwgYmluLnNpemUgPSA1MDAwKQpgYGAKIyMgQmluYXJpemUgbWF0cml4ClNvbWUgaXRlbXMgaW4gdGhlIGNvdW50IG1hdHJpeCBoYXZlIGFibm9ybWFsbHkgaGlnaCBjb3ZlcmFnZSBwZXJoYXBzIGR1ZSB0byB0aGUgYWxpZ25tZW50IGVycm9ycy4gVGhlcmVmb3JlLCB3ZSBuZXh0IHJlbW92ZSB0b3AgMC4xJSBpdGVtcyBpbiB0aGUgY291bnQgbWF0cml4IGFuZCB0aGVuIGNvbnZlcnQgdGhlIHJlbWFpbmluZyBub24temVybyB2YWx1ZXMgdG8gMS4KYGBge3J9Cnguc3AgPSBtYWtlQmluYXJ5KHguc3AsIG1hdD0iYm1hdCIpCnguc3AKYGBgCgojIyBGaWx0ZXIgYmlucwpGaWx0ZXIgb3V0IGJpbnMgb3ZlcmxhcHBpbmcgdyBFTkNPREUgYmxhY2tsaXN0CgpgYGB7cn0KYmxhY2tfbGlzdCA9IHJlYWQudGFibGUoIn4vYW5ub3RhdGlvbnMvaGczOC5ibGFja2xpc3QuYmVkLmd6IikKYmxhY2tfbGlzdC5nciA9IEdSYW5nZXMoCiAgYmxhY2tfbGlzdFssMV0sIAogIElSYW5nZXMoYmxhY2tfbGlzdFssMl0sIGJsYWNrX2xpc3RbLDNdKQopOwppZHkgPSBxdWVyeUhpdHMoCiAgZmluZE92ZXJsYXBzKHguc3BAZmVhdHVyZSwgYmxhY2tfbGlzdC5ncikKKTsKaWYobGVuZ3RoKGlkeSkgPiAwKXsKICB4LnNwID0geC5zcFssLWlkeSwgbWF0PSJibWF0Il07Cn07Cnguc3AKYGBgCgpFeGNsdWRlIGJhZCBjaHJvbW9zb21lcwpgYGB7cn0KY2hyLmV4Y2x1ZGUgPSBzZXFsZXZlbHMoeC5zcEBmZWF0dXJlKVtncmVwKCJyYW5kb218Y2hyTSIsIHNlcWxldmVscyh4LnNwQGZlYXR1cmUpKV0KaWR5ID0gZ3JlcChwYXN0ZShjaHIuZXhjbHVkZSwgY29sbGFwc2U9InwiKSwgeC5zcEBmZWF0dXJlKQppZihsZW5ndGgoaWR5KSA+IDApewogIHguc3AgPSB4LnNwWywtaWR5LCBtYXQ9ImJtYXQiXQp9Cnguc3AKYGBgCgpyZW1vdmUgdGhlIHRvcCA1JSBiaW5zIHRoYXQgb3ZlcmxhcCB3aXRoIGludmFyaWFudCBmZWF0dXJlcyBzdWNoIGFzIHRoZSBob3VzZSBrZWVwaW5nIGdlbmUgcHJvbW90ZXJzCgpgYGB7cn0KYmluLmNvdiA9IGxvZzEwKE1hdHJpeDo6Y29sU3Vtcyh4LnNwQGJtYXQpKzEpCiMgYmluLmNvdiA9IE1hdHJpeDo6Y29sU3Vtcyh4LnNwQGJtYXQpCmhpc3QoCiAgYmluLmNvdltiaW4uY292ID4gMF0sIAogIHhsYWI9ImxvZzEwKGJpbiBjb3YpIiwgCiAgbWFpbj0ibG9nMTAoQmluIENvdikiLCAKICBjb2w9ImxpZ2h0Ymx1ZSIsIAogICMgeGxpbT1jKDAsIDUpLAogIGJyZWFrcz0xMDAKKTsKYmluLmN1dG9mZiA9IHF1YW50aWxlKGJpbi5jb3ZbYmluLmNvdiA+IDBdLCAwLjk1KQppZHkgPSB3aGljaChiaW4uY292IDw9IGJpbi5jdXRvZmYgJiBiaW4uY292ID4gMCkKeC5zcCA9IHguc3BbLCBpZHksIG1hdD0iYm1hdCJdOwp4LnNwCmBgYAoKUmVtb3ZlIGFueSBjZWxscyBvZiBiaW4gY292ZXJhZ2UgbGVzcyB0aGFuIDEsMDAwLiBUaGUgcmF0aW9uYWwgYmVoaW5kIHRoaXMgaXMgdGhhdCBzb21lIGNlbGxzIG1heSBoYXZlIGhpZ2ggbnVtYmVyIG9mIHVuaXF1ZSBmcmFnbWVudHMgYnV0IGVuZCB1cCB3aXRoIGxvdyBiaW4gY292ZXJhZ2UgYWZ0ZXIgZmlsdGVyaW5nLiBUaGlzIHN0ZXAgaXMgb3B0aW9uYWwgYnV0IGhpZ2hseSByZWNvbW1hbmRlZC4KYGBge3J9CmlkeCA9IHdoaWNoKE1hdHJpeDo6cm93U3Vtcyh4LnNwQGJtYXQpID4gMTAwMCk7Cnguc3AgPSB4LnNwW2lkeCxdOwp4LnNwCmBgYAoKIyMgRGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uClVzZXMgZGlmZnVzaW9uIG1hcCBhbGdvcml0aG0gdyBzYW1wbGluZyB0ZWNobmlxdWUgdG8gbWFrZSBpdCBmYXN0LgoKYGBge3J9CiMjIFNhbXBsZSAxMDAgY2VsbHMgYXMgbGFuZG1hcmtzIApyb3cuY292cy5kZW5zIDwtIGRlbnNpdHkoCiAgeCA9IHguc3BAbWV0YURhdGFbLCJsb2dVTUkiXSwgCiAgYncgPSAnbnJkJywgYWRqdXN0ID0gMQopCnNhbXBsaW5nX3Byb2IgPC0gMSAvIChhcHByb3goeCA9IHJvdy5jb3ZzLmRlbnMkeCwgeSA9IHJvdy5jb3ZzLmRlbnMkeSwgeG91dCA9IHguc3BAbWV0YURhdGFbLCJsb2dVTUkiXSkkeSArIC5NYWNoaW5lJGRvdWJsZS5lcHMpOwpzZXQuc2VlZCgxKQppZHgubGFuZG1hcmsuZHMgPC0gc29ydChzYW1wbGUoeCA9IHNlcShucm93KHguc3ApKSwgc2l6ZSA9IDEwMDAsIHByb2IgPSBzYW1wbGluZ19wcm9iKSkKCiMjIFNwbGl0IGJldHdlZW4gbGFuZG1hcmsgYW5kIHF1ZXJ5IGNlbGxzCngubGFuZG1hcmsuc3AgPSB4LnNwW2lkeC5sYW5kbWFyay5kcyxdOwp4LnF1ZXJ5LnNwID0geC5zcFstaWR4LmxhbmRtYXJrLmRzLF07CgojIyBSdW4gZGlmZnVzaW9uIG1hcCBvbiBsYW5kbWFyawp4LmxhbmRtYXJrLnNwID0gcnVuRGlmZnVzaW9uTWFwcygKICBvYmo9IHgubGFuZG1hcmsuc3AsCiAgaW5wdXQubWF0PSJibWF0IiwgCiAgbnVtLmVpZ3M9NTAKKTsKeC5sYW5kbWFyay5zcEBtZXRhRGF0YSRsYW5kbWFyayA9IDE7CgojIyBQcm9qZWN0IHF1ZXJ5IGNlbGxzCngucXVlcnkuc3AgPSBydW5EaWZmdXNpb25NYXBzRXh0ZW5zaW9uKAogIG9iajE9eC5sYW5kbWFyay5zcCwgCiAgb2JqMj14LnF1ZXJ5LnNwLAogIGlucHV0Lm1hdD0iYm1hdCIKKQp4LnF1ZXJ5LnNwQG1ldGFEYXRhJGxhbmRtYXJrID0gMDsKCiMjIENvbWJpbmUgCnguc3AgPSBzbmFwUmJpbmQoeC5sYW5kbWFyay5zcCwgeC5xdWVyeS5zcCk7Cnguc3AgPSB4LnNwW29yZGVyKHguc3BAbWV0YURhdGFbLCJzYW1wbGUiXSldOyAjSU1QT1JUQU5UCmBgYAoKVG8gZGV0ZXJtaW5lIHNpZ25pZmljYW50IGRpZmZ1c2lvbiBjb21wb25lbnRzOgo+IFdlIHVzZSBhbiBhZCBob2MgbWV0aG9kIGJ5IHNpbXBseSBsb29raW5nIGF0IGEgcGFpcndpc2UgcGxvdCBhbmQgc2VsZWN0IHRoZSBudW1iZXIgb2YgZWlnZW4gdmVjdG9ycyB0aGF0IHRoZSBzY2F0dGVyIHBsb3Qgc3RhcnRzIGxvb2tpbmcgbGlrZSBhIGJsb2IuIEluIHRoZSBiZWxvdyBleGFtcGxlLCB3ZSBjaG9vc2UgdGhlIGZpcnN0IDE1IGVpZ2VuIHZlY3RvcnMuCgpgYGB7cn0KcGxvdERpbVJlZHVjdFBXKAogICAgb2JqPXguc3AsIAogICAgZWlncy5kaW1zPTE6NTAsCiAgICBwb2ludC5zaXplPTAuMywKICAgIHBvaW50LmNvbG9yPSJncmV5IiwKICAgIHBvaW50LnNoYXBlPTE5LAogICAgcG9pbnQuYWxwaGE9MC42LAogICAgZG93bi5zYW1wbGU9NTAwMCwKICAgIHBkZi5maWxlLm5hbWU9TlVMTCwgCiAgICBwZGYuaGVpZ2h0PTcsIAogICAgcGRmLndpZHRoPTcKICApOwpgYGAKCiMjIENsdXN0ZXJpbmcgYW5kIHZpc3VhbGl6YXRpb24KYGBge3J9CnNpZ25pZi5kaW1zID0gMToxNgp4LnNwID0gcnVuS05OKAogICAgb2JqPXguc3AsCiAgICBlaWdzLmRpbXM9c2lnbmlmLmRpbXMsCiAgICBrPTE1CiAgKTsKCnguc3A9cnVuQ2x1c3RlcigKICAgIG9iaj14LnNwLAogICAgdG1wLmZvbGRlcj10ZW1wZGlyKCksCiAgICBsb3V2YWluLmxpYj0ibGVpZGVuIiwKICAgIHNlZWQudXNlPTEwLAogICAgcmVzb2x1dGlvbj0wLjcKICApOwpgYGAKClZpc3VhbGl6YXRpb24KYGBge3IsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTV9Cnguc3AgPSBydW5WaXooCiBvYmo9eC5zcCwgCiB0bXAuZm9sZGVyPXRlbXBkaXIoKSwKIGRpbXM9MiwKIGVpZ3MuZGltcz1zaWduaWYuZGltcywgCiBtZXRob2Q9InVtYXAiLAogc2VlZC51c2U9MTAKKQoKcGxvdFZpeigKICAgIG9iaj0geC5zcCwKICAgIG1ldGhvZD0idW1hcCIsIAogICAgbWFpbj0iQ2x1c3RlciIsCiAgICBwb2ludC5jb2xvcj14LnNwQGNsdXN0ZXIsIAogICAgcG9pbnQuc2l6ZT0wLjIsIAogICAgcG9pbnQuc2hhcGU9MTksIAogICAgdGV4dC5hZGQ9VFJVRSwKICAgIHRleHQuc2l6ZT0xLAogICAgdGV4dC5jb2xvcj0iYmxhY2siLAogICAgZG93bi5zYW1wbGU9MTAwMDAsCiAgICBsZWdlbmQuYWRkPUZBTFNFCiAgKTsKCmBgYAoKCjwhLS0gIyMgT3V0bGllciBhbmFseXNpcyAtLT4KPCEtLSBgYGB7cn0gLS0+CjwhLS0gZGF0YS5mcmFtZSh4LnNwQHVtYXApICU+JSAtLT4KPCEtLSAgIG11dGF0ZShjZWxsPXguc3BAYmFyY29kZSkgJT4lIC0tPgo8IS0tICAgIyBhcnJhbmdlKHVtYXAuMSkgLS0+CjwhLS0gICBnZ3Bsb3QoYWVzKHVtYXAuMSwgdW1hcC4yKSkgKyAtLT4KPCEtLSAgIGdlb21fcG9pbnQoc2l6ZT0wLjIpIC0tPgo8IS0tIGBgYCAtLT4KPCEtLSBgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTV9IC0tPgo8IS0tIG91dGxpZXIuaXguMSA8LSBkYXRhLmZyYW1lKHguc3BAdW1hcCkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKGNlbGw9eC5zcEBiYXJjb2RlKSAlPiUgLS0+CjwhLS0gICByb3dpZF90b19jb2x1bW4oKSAlPiUgLS0+CjwhLS0gICBkcGx5cjo6ZmlsdGVyKHVtYXAuMSA+IDEwMCkgJT4lIC0tPgo8IS0tICAgcHVsbChyb3dpZCkgLS0+CjwhLS0gb3V0bGllci5peC4yIDwtIGRhdGEuZnJhbWUoeC5zcEB1bWFwKSAlPiUgLS0+CjwhLS0gICBtdXRhdGUoY2VsbD14LnNwQGJhcmNvZGUpICU+JSAtLT4KPCEtLSAgIHJvd2lkX3RvX2NvbHVtbigpICU+JSAtLT4KPCEtLSAgIGRwbHlyOjpmaWx0ZXIodW1hcC4yIDwgLTUwKSAlPiUgLS0+CjwhLS0gICBwdWxsKHJvd2lkKSAtLT4KCjwhLS0gc21wLm90aGVyIDwtIHNhbXBsZShzZXRkaWZmKDE6bGVuZ3RoKHguc3BAYmFyY29kZSksIGMob3V0bGllci5peC4xLCBvdXRsaWVyLml4LjIpKSwgMTAwKSAtLT4KCjwhLS0gbG9uZy5vdXRsaWVyLmJtYXQgPC0gLS0+CjwhLS0gICB4LnNwQGJtYXRbYyhvdXRsaWVyLml4LjEsIG91dGxpZXIuaXguMiwgc21wLm90aGVyKSxdICU+JSAtLT4KPCEtLSAgIGFzLm1hdHJpeCgpICU+JSAtLT4KPCEtLSAgIG1lbHQodmFsdWUubmFtZSA9ICJhY2MiLCB2YXJuYW1lcz1jKCJiYXJjb2RlIiwgImJpbiIpKSAtLT4KCjwhLS0gIyBsb25nLm91dGxpZXIuYm1hdCAlPiUgLS0+CjwhLS0gIyAgICMgZHBseXI6OmZpbHRlcihiYXJjb2RlPT0nQUFDQUFBR0dUQUdBQ0dDQS0xJykgJT4lIC0tPgo8IS0tICMgICBtdXRhdGUoYmlnLmJpbnM9Y3V0KGJpbixicmVha3MgPSAxMDAwMCkpICU+JSAtLT4KPCEtLSAjICAgZ3JvdXBfYnkoYmFyY29kZSkgJT4lIC0tPgo8IS0tICMgICBhcnJhbmdlKGJpbikgJT4lIC0tPgo8IS0tICMgICBtdXRhdGUocnVubWVhbj1yb2xsbWVhbihhY2MsayA9IDEwMDAwKVsxOm4oKV0pICU+JSAtLT4KPCEtLSAjICAgdW5ncm91cCgpICU+JSAtLT4KPCEtLSAjICAgZ3JvdXBfYnkoYmFyY29kZSwgYmlnLmJpbnMpICU+JSAtLT4KPCEtLSAjICAgc3VtbWFyaXNlKGJpbi5tZWFuPW1lYW4ocnVubWVhbikpICU+JSAtLT4KPCEtLSAjICAgbXV0YXRlKGJpZy5iaW5zPWFzLm51bWVyaWMoYmlnLmJpbnMpKSAlPiUgLS0+CjwhLS0gIyAgIG11dGF0ZShvdXRsaWVyPWlmZWxzZShiYXJjb2RlICVpbiUgeC5zcEBiYXJjb2RlW291dGxpZXIuaXhdLCBUUlVFLCBGQUxTRSkpICU+JSAtLT4KPCEtLSAjICAgZ2dwbG90KGFlcyggYmlnLmJpbnMsIGJhcmNvZGUsIGZpbGw9YmluLm1lYW4pKSArIC0tPgo8IS0tICMgICBnZW9tX3RpbGUoKSArIC0tPgo8IS0tICMgICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsgLS0+CjwhLS0gIyAgIGZhY2V0X3dyYXAob3V0bGllcn4uLCBzY2FsZXM9ImZyZWVfeSIsIG5jb2w9MSwgbnJvdz0yKSAtLT4KPCEtLSAjICAgdW5ncm91cCgpICU+JSAtLT4KPCEtLSAjICAgZ2dwbG90KGFlcyhiaW4sIHJ1bm1lYW4pKSArIC0tPgo8IS0tICMgICBmYWNldF93cmFwKGJpZy5iaW5zfi4sIHNjYWxlPSJmcmVlX3giLCBuY29sPTIpICsgLS0+CjwhLS0gIyAgIGdlb21fbGluZShhZXMoZ3JvdXA9YmFyY29kZSwgY29sb3I9b3V0bGllciksIHNpemU9MC41LCBhbHBoYT0wLjUpIC0tPgo8IS0tICMgICAgLS0+CjwhLS0gYGBgIC0tPgoKPCEtLSBgYGB7cn0gLS0+CjwhLS0gbG9uZy5vdXRsaWVyLmJtYXQgJT4lIC0tPgo8IS0tICAgZ3JvdXBfYnkoYmFyY29kZSkgJT4lIC0tPgo8IS0tICAgc3VtbWFyaXNlKGFjYz1zdW0oYWNjKSkgJT4lIC0tPgo8IS0tICAgbXV0YXRlKG91dGxpZXI9Y2FzZV93aGVuKGJhcmNvZGUgJWluJSB4LnNwQGJhcmNvZGVbb3V0bGllci5peC4xXSB+ICAib3V0MSIsICAtLT4KPCEtLSAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYXJjb2RlICVpbiUgeC5zcEBiYXJjb2RlW291dGxpZXIuaXguMl0gfiAib3V0MiIsIC0tPgo8IS0tICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZBTFNFIH4gJ290aGVyJykpICU+JSAtLT4KPCEtLSAgICMgbXV0YXRlKG91dGxpZXI9aWZlbHNlKGJhcmNvZGUgJWluJSB4LnNwQGJhcmNvZGVbb3V0bGllci5peC4yXSwgIm91dDIiLCBGQUxTRSkpICU+JSAtLT4KPCEtLSAgIGdncGxvdChhZXMob3V0bGllciwgYWNjKSkgKyAtLT4KPCEtLSAgIGdnYmVlc3dhcm06Omdlb21fcXVhc2lyYW5kb20oKSAtLT4KPCEtLSBgYGAgLS0+CgoKIyMgTWFrZSBnZW5lIG1hdHJpeApgYGB7cn0KdHJhbnNjcmlwdHMuZ3IgPSBydHJhY2tsYXllcjo6aW1wb3J0KCJ+L0hvbW9fc2FwaWVucy5HUkNoMzguOTMuZmlsdGVyZWQuZ3RmIikKY29sbmFtZXModHJhbnNjcmlwdHMuZ3JAZWxlbWVudE1ldGFkYXRhKSA8LSBzdHJfcmVwbGFjZShjb2xuYW1lcyh0cmFuc2NyaXB0cy5nckBlbGVtZW50TWV0YWRhdGEpLCAiZ2VuZV9uYW1lIiwgIm5hbWUiKQoKZ2VuZXMuZ3IgPC0gdW5saXN0KHJhbmdlKHNwbGl0KHRyYW5zY3JpcHRzLmdyLCB+IG5hbWUpKSkgICMjIEZyb20gdHJhbnNjcmlwdHMgdG8gZ2VuZXMKZ2VuZXMuZ3IkbmFtZSA8LSBuYW1lcyhnZW5lcy5ncikKCmlmIChHZW5vbWVJbmZvRGI6OnNlcWxldmVsc1N0eWxlKGdlbmVzLmdyKSAhPSBHZW5vbWVJbmZvRGI6OnNlcWxldmVsc1N0eWxlKHguc3BAZmVhdHVyZSkgKSB7CiAgR2Vub21lSW5mb0RiOjpzZXFsZXZlbHNTdHlsZShnZW5lcy5ncikgPC0gR2Vub21lSW5mb0RiOjpzZXFsZXZlbHNTdHlsZSh4LnNwQGZlYXR1cmUpCn0KCnguc3AgPSBjcmVhdGVHbWF0RnJvbU1hdCgKICAgIG9iaj14LnNwLCAKICAgIGlucHV0Lm1hdD0iYm1hdCIsCiAgICBnZW5lcz1nZW5lcy5nciwKICAgIGRvLnBhcj1UUlVFLAogICAgbnVtLmNvcmVzPTEwCiAgKQoKc2F2ZVJEUyh4LnNwLCBmaWxlID0gIn4vbXlfZGF0YS9jZWxscmFuZ2VyLWF0YWMxMTBfY291bnRfMzA0MzlfV1NTUzgwMzgzNjBfR1JDaDM4LTFfMV8wLnNuYXBBVEFDLlJEUyIpCgpgYGAKClJlcGVhdCB2aXN1YWxpemF0aW9uIG9uIGdtYXQKYGBge3J9CiMjIE1ha2Ugc3VlcmF0IG9iamVjdApmNzQuc2V1IDwtIHNuYXBUb1NldXJhdCgKICAgIG9iaj14LnNwLCAKICAgIGVpZ3MuZGltcz0xOjIwLCAKICAgIG5vcm09VFJVRSwKICAgIHNjYWxlPVRSVUUKICAgICkKCiMjIFNhdmUgVU1BUCBiYXNlZCBvbiBTbmFwQVRBQyBwcm9jZXNzaW5nCmY3NC5zZXUgPC0gUnVuVU1BUChmNzQuc2V1LCByZWR1Y3Rpb24gPSAiU25hcEFUQUMiLCBkaW1zPXNpZ25pZi5kaW1zLCByZWR1Y3Rpb24ubmFtZSA9ICJ1bWFwLnNuYXAiKQoKCmY3NC5zZXUgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoZjc0LnNldSkKZjc0LnNldSA8LSBSdW5QQ0EoZjc0LnNldSwgZGltcz0xOjUwKQpmNzQuc2V1IDwtIFJ1blVNQVAoZjc0LnNldSwgcmVkdWN0aW9uID0gInBjYSIsIGRpbXM9MTo1MCwgdmVyYm9zZT1GQUxTRSkgCgpnZ3B1YnI6OmdnYXJyYW5nZSgKICBEaW1QbG90KGY3NC5zZXUsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSBOVUxMKSArIGdndGl0bGUoImdtYXQiKSwKICBEaW1QbG90KGY3NC5zZXUsIHJlZHVjdGlvbiA9ICJ1bWFwLnNuYXAiLCBncm91cC5ieSA9IE5VTEwpICsgZ2d0aXRsZSgiYm1hdCAtIHNuYXAiKSAKICApCgpgYGAKCgpgYGB7cn0KcGxvdGx5OjpnZ3Bsb3RseShEaW1QbG90KGY3NC5zZXUsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSBOVUxMKSArIGdndGl0bGUoImdtYXQiKSkKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0xNSwgZmlnLndpZHRoPTE1fQpGZWF0dXJlUGxvdChmNzQuc2V1LCBmZWF0dXJlcyA9IGMoJ1BUUFJDJywnQ0Q0JywnQ0Q4QScsJ0NEOEInLCdDRDc5QScsJ0ZPWE4xJywnRVBDQU0nLCdQREdGUkEnLCdHTkc0JywgJ0ZPWFAzJywnUkFHMScsJ1JBRzInLCdOS0c3JywnQ0NSNycpLCByZWR1Y3Rpb24gPSAidW1hcC5zbmFwIikKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0xNSwgZmlnLndpZHRoPTE1fQpEaW1IZWF0bWFwKGY3NC5zZXUsIGRpbXMgPSAxOjYsIGJhbGFuY2VkID0gVFJVRSxuY29sID0gMSkKYGBgCgojIyMgRGlmZmVyZW50aWFsIGFuYWx5c2lzIAo8IS0tIFNuYXAgQVRBQyB3YXkgLS0+CjwhLS0gYGBge3J9IC0tPgo8IS0tIGNsdXN0ZXIgPSAxIC0tPgo8IS0tIERBUnM9IGZpbmREQVIoIC0tPgo8IS0tICAgICBvYmo9eC5zcCwgLS0+CjwhLS0gICAgIGlucHV0Lm1hdD0iZ21hdCIsIC0tPgo8IS0tICAgICBjbHVzdGVyLnBvcz1jbHVzdGVyLCAtLT4KPCEtLSAgICAgIyBjbHVzdGVyLm5lZyA9IDUsIC0tPgo8IS0tICAgICBjbHVzdGVyLm5lZy5tZXRob2Q9InJhbmRvbSIsIC0tPgo8IS0tICAgICB0ZXN0Lm1ldGhvZD0iZXhhY3RUZXN0IiwgLS0+CjwhLS0gICAgIGJjdj0wLjQsICMwLjQgZm9yIGh1bWFuLCAwLjEgZm9yIG1vdXNlIC0tPgo8IS0tICAgICBzZWVkLnVzZT00MiAtLT4KPCEtLSAgICk7IC0tPgoKCjwhLS0gZ21hdC5jbCA8LSB4LnNwQGdtYXRbd2hpY2goeC5zcEBjbHVzdGVyPT1jbHVzdGVyKSxdIC0tPgo8IS0tIGdtYXQubmVnIDwtIHguc3BAZ21hdFt3aGljaCh4LnNwQGNsdXN0ZXIhPWNsdXN0ZXIpLF0gLS0+Cgo8IS0tIGhpc3QoYXMubWF0cml4KGdtYXQuY2wpLCBicmVha3M9NTApIC0tPgoKCjwhLS0gIyBEQVJzICU+JSAtLT4KPCEtLSAgICMgbXV0YXRlKGdlbmU9Y29sbmFtZXMoeC5zcEBnbWF0KSkgJT4lIC0tPgo8IS0tICAgIyBhcnJhbmdlKCkgJT4lIC0tPgo8IS0tICAgIyBmaWx0ZXIoUFZhbHVlIDwgMC4wNSkgLS0+Cgo8IS0tIGhpc3QoREFScyRQVmFsdWUsIGJyZWFrcyA9IDIwKSAtLT4KCjwhLS0gREFScyRGRFIgPSBwLmFkanVzdChEQVJzJFBWYWx1ZSwgbWV0aG9kPSJCSCIpIC0tPgo8IS0tIGlkeSA9IHdoaWNoKERBUnMkRkRSIDwgNWUtMiAmIERBUnMkbG9nRkMgPiAwKSAtLT4KPCEtLSBwbG90KERBUnMkbG9nQ1BNLCBEQVJzJGxvZ0ZDLCAgLS0+CjwhLS0gICAgIHBjaD0xOSwgY2V4PTAuMSwgY29sPSJncmV5IiwgIC0tPgo8IS0tICAgICB5bGFiPSJsb2dGQyIsIHhsYWI9ImxvZ0NQTSIsIC0tPgo8IS0tICAgICBtYWluPXBhc3RlKCJDbHVzdGVyIiwgY2x1c3RlcikgLS0+CjwhLS0gICApIC0tPgo8IS0tIHBvaW50cyhEQVJzJGxvZ0NQTVtpZHldLCAgLS0+CjwhLS0gICAgIERBUnMkbG9nRkNbaWR5XSwgIC0tPgo8IS0tICAgICBwY2g9MTksICAtLT4KPCEtLSAgICAgY2V4PTAuNSwgIC0tPgo8IS0tICAgICBjb2w9InJlZCIgLS0+CjwhLS0gICApIC0tPgoKPCEtLSBgYGAgLS0+CmBgYHtyfQpEQUdzX2NsIDwtIEZpbmRBbGxNYXJrZXJzKGY3NC5zZXUsIG1pbi5wY3QgPSAwLjIsIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMywgb25seS5wb3MgPSBUUlVFKQpEQUdzX2NsIDwtIAogIERBR3NfY2wgJT4lCiAgbXV0YXRlKHBvc2l0aXZlPWF2Z19sb2dGQyA+IDApCmBgYApgYGB7ciwgZmlnLmhlaWdodD0xNCwgZmlnLndpZHRoPTl9CnRvcDEwX21hcmtlcnMgPC0gREFHc19jbCAlPiUKICBmaWx0ZXIocG9zaXRpdmUpICU+JQogIGdyb3VwX2J5KGNsdXN0ZXIpICU+JQogIGFycmFuZ2UocF92YWxfYWRqKSAlPiUKICB0b3BfbigxMCkgJT4lCiAgcHVsbChnZW5lKSAlPiUKICB1bmlxdWUoKQoKdG9wMTBfbWFya2VycyA8LSBEQUdzX2NsICU+JQogIGZpbHRlcihwb3NpdGl2ZSkgJT4lCiAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lCiAgIyBhcnJhbmdlKHBfdmFsX2FkaikgJT4lCiAgdG9wX24oMTAsd3QgPSAtIHBfdmFsX2FkaikgJT4lCiAgcHVsbChnZW5lKQoKRG90UGxvdChmNzQuc2V1LCBmZWF0dXJlcyA9IHRvcDEwX21hcmtlcnMpICsKICBjb29yZF9mbGlwKCkKYGBgCgpFeHByZXNzaW9uIG9mIG1hcmtlcnMgaW4gUk5BIGRhdGEKCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTZ9CnBsb3RfbWFya2VyX0FUQUN2c1JOQSA8LSBmdW5jdGlvbihtYXJrZXIpewogICAgZ2dwdWJyOjpnZ2FycmFuZ2UoCiAgICAgIEZlYXR1cmVQbG90KGY3NC5zZXUsIGZlYXR1cmU9bWFya2VyLCByZWR1Y3Rpb249InVtYXAuc25hcCIpICsgZ2d0aXRsZSgiQVRBQyIpLCBGZWF0dXJlUGxvdChmNzQucm5hLnNldSwgZmVhdHVyZT1tYXJrZXIpICsgZ2d0aXRsZSgiUk5BIikKICAgICkgJT4lIGFubm90YXRlX2ZpZ3VyZSh0b3A9bWFya2VyKQogIH0KCnBsb3RfY2x1c3Rlcl9tYXJrZXJfQVRBQ3ZzUk5BIDwtIGZ1bmN0aW9uKGNsKXsKICB0b3AxMF9jbHVzdGVyIDwtCiAgICBEQUdzX2NsICU+JQogICAgZmlsdGVyKHBvc2l0aXZlKSAlPiUKICAgIGZpbHRlcihjbHVzdGVyPT1jbCkgJT4lCiAgICBmaWx0ZXIoZ2VuZSAlaW4lIHJvd25hbWVzKGY3NC5ybmEuc2V1KSkgJT4lCiAgICB0b3BfbigxMCx3dCA9IC0gcF92YWxfYWRqKSAlPiUKICAgIHB1bGwoZ2VuZSkKICBnZ2FycmFuZ2UocGxvdGxpc3QgPSBtYXAodG9wMTBfY2x1c3RlciwgfiBwbG90X21hcmtlcl9BVEFDdnNSTkEoLngpKSwgbmNvbD0yLCBucm93PTUpICU+JSAKICAgIGFubm90YXRlX2ZpZ3VyZShmaWcubGFiPXBhc3RlKCJDbHVzdGVyIiwgY2wpLCBmaWcubGFiLnNpemUgPSAyMCwgZmlnLmxhYi5mYWNlID0gImJvbGQiKQogIH0KCm1hcCgxOjEwLCB+IHBsb3RfY2x1c3Rlcl9tYXJrZXJfQVRBQ3ZzUk5BKC54KSkKYGBgCgpgYGB7cn0KcGxvdGx5OjpnZ3Bsb3RseShEaW1QbG90KGY3NC5ybmEuc2V1LCBncm91cC5ieSA9ICJhbm5vdGF0aW9uIikpCmBgYAoKCiMjIEVtYmVkZGluZyBvbiBtb3N0IHZhcmlhYmxlIGdlbmVzIGluIEFUQUMgKG5vdCBpbiBnZW5lIGV4cHJlc3Npb24pCmBgYHtyfQpEZWZhdWx0QXNzYXkoZjc0LnNldSkgPC0gIkFDVElWSVRZIgpmNzQuc2V1Lmh2Z2F0YWMgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoZjc0LnNldSkKZjc0LnNldS5odmdhdGFjIDwtIFJ1blBDQShmNzQuc2V1Lmh2Z2F0YWMsIGRpbXM9MTozMCkKZjc0LnNldS5odmdhdGFjIDwtIFJ1blVNQVAoZjc0LnNldS5odmdhdGFjLCByZWR1Y3Rpb24gPSAicGNhIiwgZGltcz0xOjMwLCB2ZXJib3NlPUZBTFNFKSAKCmY3NC5zZXUuaHZnYXRhYyA8LSBBZGRNZXRhRGF0YShmNzQuc2V1Lmh2Z2F0YWMsIHguc3BAY2x1c3RlciwgY29sLm5hbWU9J1NuYXBBVEFDX2NsdXN0ZXInKQpEaW1QbG90KGY3NC5zZXUuaHZnYXRhYywgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICJTbmFwQVRBQ19jbHVzdGVyIikgCmBgYAoKCiMjIFRob3VnaHRzCgotIFRoZXJlIHNlZW1zIHRvIGJlIGEgY29ycmVzcG9uZGFuY2UgZm9yIGdlbmUgZXhwcmVzc2lvbiBhbmQgYWNjZXNzaWJpbGl0eSBpbiBtYW55ICJhY2Nlc3NpYmlsaXR5IG1hcmtlcnMiIChzZWUgQ2x1c3RlciA2KSAtLT4gbWF5YmUgaW50ZWdyYXRpb24gd291bGQgd29yayBiZXR0ZXIgaWYgSSBkb24ndCB0YWtlIG9ubHkgdGhlIG1vc3QgdmFyaWFibGUgZmVhdHVyZXMgaW4gdGhlIFJOQSBkYXRhLiAKCgoK